package org.example.leetcode.core.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.text.StrSubstitutor;
import org.example.leetcode.codeExecutor.api.CodeExecutorService;
import org.example.leetcode.common._enum.*;
import org.example.leetcode.common.domain.CodeDO;
import org.example.leetcode.common.domain.CodeResult;
import org.example.leetcode.common.domain.SysUser;
import org.example.leetcode.common.exception.MyException;
import org.example.leetcode.common.pojo.FileVo;
import org.example.leetcode.common.pojo.GlobalResponse;
import org.example.leetcode.common.pojo.PageCond;
import org.example.leetcode.common.pojo.PageResult;
import org.example.leetcode.common.utils.TitleUtils;
import org.example.leetcode.common.utils.UserInfo;
import org.example.leetcode.core.api.TQuestionPublicService;
import org.example.leetcode.core.aop.QuestionOnClickAop;
import org.example.leetcode.core.dao.*;
import org.example.leetcode.core.domain.*;
import org.example.leetcode.core.dto.*;
import org.example.leetcode.core.service.TQuestionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.example.leetcode.core.service.TQuestionTypeRelationService;
import org.example.leetcode.core.service.TQuestionTypeService;
import org.example.leetcode.core.service.UserQuestionClickService;
import org.example.leetcode.core.utils.CommonUtils;
import org.example.leetcode.core.utils.RedisUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zhc
 * @since 2024-02-25
 */
@Service
@Slf4j
public class TQuestionServiceImpl extends ServiceImpl<TQuestionMapper, TQuestion> implements TQuestionService, TQuestionPublicService {

    @Resource
    DailyQuestionMapper dailyQuestionMapper;
    @Resource
    TQuestionMapper tQuestionMapper;
    @Resource
    TQuestionTypeService tQuestionTypeService;
    @Resource
    TQuestionTypeRelationService tQuestionTypeRelationService;
    @Resource
    CodeExecutorService codeExecutorService;
    @Resource
    TQuestionTypeRelationMapper tQuestionTypeRelationMapper;
    @Resource
    UserQuestionLogMapper userQuestionLogMapper;
    @Resource
    RedisUtils redisUtils;
    @Resource
    RedisTemplate redisTemplate;
    @Resource
    UserQuestionClickService userQuestionClickService;
    @Resource
    SysUserMapper sysUserMapper;
    @Override
    public PageResult<QuestionPageVO> page(QuestionPageCond questionPageCond) {
        Page<QuestionPageVO> page=new Page<>(questionPageCond.getPageNo(),questionPageCond.getPageSize());
        if(!CollectionUtils.isEmpty(questionPageCond.getTypes())){
            questionPageCond.setTypeLength(questionPageCond.getTypes().size());
        }
        Page<QuestionPageVO> pageVOPage= tQuestionMapper.page(page,questionPageCond);
        PageResult<QuestionPageVO> res = new PageResult<>();
        BeanUtils.copyProperties(pageVOPage,res);
        return res;
    }

    @Override
    public TQuestion getQuestionInfo(String titleSlug) {
        TQuestion tQuestion = this.getBaseMapper().selectOne(new LambdaQueryWrapper<TQuestion>()
                .eq(TQuestion::getTitleSlug, titleSlug));
        log.info(titleSlug);
        if(tQuestion==null){
            throw new MyException("数据不存在");
        }
        tQuestion.setWatchedTimes(tQuestion.getWatchedTimes()==null?1: tQuestion.getWatchedTimes()+1);
        this.saveOrUpdate(tQuestion);
        return tQuestion;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(TQuestionAddDto tQuestion) {
        if(tQuestion.getId()==null){
            TQuestion t = this.getBaseMapper().selectOne(new LambdaQueryWrapper<TQuestion>()
                    .eq(TQuestion::getTitleSlug, tQuestion.getTitleSlug()));
            if(t!=null){
                throw new MyException("题目名称重复");
            }
        }
        checkParams(tQuestion);
        TQuestion question = new TQuestion();
        BeanUtils.copyProperties(tQuestion,question);
        question.setType(tQuestion.getTypes());
        this.saveOrUpdate(question);

        //保存类型相关表
        List<TQuestionTypeRelation> tQuestionTypeRelationList=new ArrayList<>();
        //先把原先的类型关联全部删除，这里不要逻辑删除了，直接删
        tQuestionTypeRelationMapper.deleteByQuestionId(question.getId());
        String[] types = tQuestion.getTypes().split(",");
        for (String type : types) {
            Long typeId=Long.valueOf(type);
            TQuestionType tQuestionType = tQuestionTypeService.getById(typeId);
            if(tQuestionType==null){
                throw new MyException("类型不存在");
            }
            TQuestionTypeRelation tQuestionTypeRelation = new TQuestionTypeRelation();
            tQuestionTypeRelation.setQuestionId(question.getId());
            tQuestionTypeRelation.setTypeId(typeId);
            tQuestionTypeRelationList.add(tQuestionTypeRelation);
        }
        tQuestionTypeRelationService.saveBatch(tQuestionTypeRelationList);


//        codeExecutorService.uploadFileToMinio(tQuestion.getAnswerFile(),tQuestion.getAnswerUrl());
//        codeExecutorService.uploadFileToMinio(tQuestion.getTestFile(),tQuestion.getTestUrl());
    }
    private FileVo getFileVo(String url){
        FileVo fileVo = new FileVo();
        try {
            fileVo.setUrl(url);
            fileVo.setName(url.substring(url.lastIndexOf("/")+1));
        }
        catch (Exception e){
            return fileVo;
        }

        return fileVo;
    }

    @Override
    public TQuestionAddDto getManagerQuestionInfo(String titleSlug) {
        TQuestion tQuestion = this.baseMapper.selectOne(new LambdaQueryWrapper<TQuestion>()
                .eq(TQuestion::getTitleSlug, titleSlug));
        if(tQuestion==null){
            return new TQuestionAddDto();
        }
        TQuestionAddDto tQuestionAddDto = new TQuestionAddDto();
        BeanUtils.copyProperties(tQuestion,tQuestionAddDto);
        tQuestionAddDto.setTypes(tQuestion.getType());
        tQuestionAddDto.setTestFile(getFileVo(tQuestionAddDto.getTestUrl()));
        tQuestionAddDto.setAnswerFile(getFileVo(tQuestionAddDto.getAnswerUrl()));
        return tQuestionAddDto;
    }

    @Override
    @Transactional
    public void delete(String titleSlug) {
        TQuestion tQuestion = this.baseMapper.selectOne(new LambdaQueryWrapper<TQuestion>()
                .eq(TQuestion::getTitleSlug, titleSlug));
        if(tQuestion==null){
            throw new MyException("数据不存在");
        }
        this.baseMapper.deleteById(tQuestion.getId());
    }

    @Override
    @Transactional
    public CodeResult executeCode(CodeDO codeDO) {
        codeDO.setPhone(UserInfo.getUserInfo().getPhone());
        TQuestion tQuestion = this.getBaseMapper().selectOne(new LambdaQueryWrapper<TQuestion>()
                .eq(TQuestion::getTitleSlug, codeDO.getTitle()));
        if(tQuestion==null){
            throw new MyException("数据不存在");
        }
        codeDO.setAnswerUrl(tQuestion.getAnswerUrl());
        codeDO.setTestUrl(tQuestion.getTestUrl());
        CodeResult data = codeExecutorService.execute(codeDO).getData();

        if(IsTestOrSubmit.SUBMIT.getCode().equals(codeDO.getIsTestOrSubmit())){
            UserQuestionLog userQuestionLog = generateSubmitLog(codeDO, data);
            userQuestionLog.setQuestionId(tQuestion.getId());
            userQuestionLogMapper.insert(userQuestionLog);
        }

        redisUtils.setZset(RedisUtils.HOT+ CommonUtils.getCurrentDate(),codeDO.getTitle(),1L, TimeUnit.DAYS);
        return data;
    }

    @Override
    public List<QuestionPageVO> queryRecommend(Integer limit) {
        SysUser userInfo = UserInfo.getUserInfo();
        if(userInfo==null){
            Set<QuestionPageVO> set=new HashSet<>();
            List<QuestionPageVO> questionPageVOS = tQuestionMapper.hotPageWithoutLogin();
            while(set.size()<limit){
                Random random = new Random();
                int i = random.nextInt(questionPageVOS.size() / 2);
                set.add(questionPageVOS.get(i));
            }
            return set.stream().collect(Collectors.toList());
        }
        List<Object> users=redisUtils.getList(RedisUtils.RECOMMEND_PERSON + userInfo.getPhone()
        ,()->
                        Arrays.asList(sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>()
                    .eq(SysUser::getPhone,userInfo.getPhone())).getRelativeUser().split(","))
                );
        //todo 获取当天火热的题目
        //推荐题和热门题按8:2
        List<QuestionPageVO> questionPageVOS=new ArrayList<>();
        if(!CollectionUtils.isEmpty(users)) {
            questionPageVOS = tQuestionMapper.queryByLikelyUser(users, userInfo.getPhone());
        }
        List<QuestionPageVO> res=new ArrayList<>();
        Set<QuestionPageVO> set=new HashSet();
        while(res.size()<limit&&questionPageVOS.size()!=set.size()){
            //热门题在前一半里随机选取
            Random r=new Random();
            int i = r.nextInt(questionPageVOS.size());
            if(!set.contains(questionPageVOS.get(i))){
                res.add(questionPageVOS.get(i));
                set.add(questionPageVOS.get(i));
            }
        }
        if(questionPageVOS.size()<limit){
            List<QuestionPageVO> pageResult = tQuestionMapper.hotPage( userInfo.getPhone(),null);
            set=new HashSet();
            while(res.size()<limit){
                //热门题在前一半里随机选取
                Random r=new Random();
                int i = r.nextInt(pageResult.size() / 2);
                if(!set.contains(pageResult.get(i))){
                    res.add(pageResult.get(i));
                    set.add(pageResult.get(i));
                }
            }
        }
        Map<Long, String> collect = tQuestionTypeService.getBaseMapper().selectList(null).stream().collect(Collectors.toMap(TQuestionType::getId, TQuestionType::getName));
        res.stream().parallel().map(item->{
            String[] split = item.getType().split(",");
            ArrayList<String> list = new ArrayList<>();
            for(String s:split){
                if(!StringUtils.isEmpty(s.trim())){
                    list.add(collect.get(Long.valueOf(s.trim())));
                }
            }
            item.setTypes(list);
            return item;
        }).collect(Collectors.toList());
        return res;
    }

    @Override
    public PageResult<DailyHotQuestion> getHot(PageCond pageCond) {
        String hotKey=RedisUtils.HOT + CommonUtils.getCurrentDate();
        PageResult<DailyHotQuestion> questionPageVOPageResult = new PageResult<>();
        Long zsetCount = redisUtils.getZsetCount(hotKey);
        questionPageVOPageResult.setTotal(zsetCount);
        Long pages=zsetCount/pageCond.getPageSize();
        Long start=pages*(pageCond.getPageNo()-1);
        List<String> zset = redisUtils.getZset(hotKey, start, start + pageCond.getPageSize());
        AtomicInteger i= new AtomicInteger(1);
        List<DailyHotQuestion> collect = zset.stream().map(item -> {
            TQuestion tQuestion = this.getBaseMapper().selectOne(new LambdaQueryWrapper<TQuestion>()
                    .eq(TQuestion::getTitleSlug, item));
            DailyHotQuestion questionPageVO = new DailyHotQuestion();
            BeanUtils.copyProperties(tQuestion, questionPageVO);
            questionPageVO.setClickNum(redisUtils.getZsetScore(hotKey,item));
            return questionPageVO;
        }).collect(Collectors.toList());
        collect.sort(Comparator.comparing(DailyHotQuestion::getClickNum).reversed());
        collect=collect.stream().map(item-> {
                    item.setOrder(i.getAndAdd(1));
                    return item;
                }).collect(Collectors.toList());
        questionPageVOPageResult.setRecords(collect);
        questionPageVOPageResult.setSize(pageCond.getPageSize());
        questionPageVOPageResult.setCurrent(pageCond.getPageNo());
        return questionPageVOPageResult;
    }

    @Override
    public List<DailyQuestionVo> getCurrentQuestion() {
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        if(UserInfo.getUserInfo()==null){
            return dailyQuestionMapper.getMyDailyQuestionLogWithoutLogin(date);
        }
        List<DailyQuestionVo> currentMonthQuestion = dailyQuestionMapper.getMyDailyQuestionLog(date,UserInfo.getUserInfo().getPhone());
        Map<String,Boolean> map=new HashMap<>();
        for (DailyQuestionVo dailyQuestionVo : currentMonthQuestion) {
            if(dailyQuestionVo.getIsFinished()){
                map.put(dailyQuestionVo.getTitleSlug(),map.getOrDefault(dailyQuestionVo.getTitleSlug(),false)||dailyQuestionVo.getIsFinished());
            }
            else{
                map.put(dailyQuestionVo.getTitleSlug(),false);
            }
        }
        List<DailyQuestionVo> res=new ArrayList<>();
        for(Map.Entry<String,Boolean> entry:map.entrySet()){
            DailyQuestionVo dailyQuestionVo = new DailyQuestionVo();
            dailyQuestionVo.setIsFinished(entry.getValue());
            dailyQuestionVo.setTitleSlug(entry.getKey());
            res.add(dailyQuestionVo);
        }
        return res;
    }

    /**
     * 基于难度，类型查找推荐题目
     * @param limit
     * @return
     */
    @Override
    public List<QuestionPageVO> queryRecommendItem(Integer limit) {
        //todo 加上调查问卷判断
        if(UserInfo.getUserInfo()==null){
            Set<QuestionPageVO> set=new HashSet<>();
            List<QuestionPageVO> questionPageVOS = tQuestionMapper.hotPageWithoutLogin();
            while(set.size()<limit){
                Random random = new Random();
                int i = random.nextInt(questionPageVOS.size() / 2);
                set.add(questionPageVOS.get(i));
            }
            return set.stream().collect(Collectors.toList());
        }
        String phone = UserInfo.getUserInfo().getPhone();
        //统计每个类型对应的权值
        Map<String,Long> typeCounts=tQuestionMapper.queryLogForTypeCount(phone)
                .stream().collect(Collectors.toMap(TypeCount::getTypeId,TypeCount::getTypeCount));
        //调用按做题记录排序的热门题目方法 todo（随机选取一部分？全部遍历太慢）
        List<QuestionPageVO> easyRecords = tQuestionMapper.hotPage(phone,DifficultyEnum.EASY.getCode());
        List<QuestionPageVO> mediumRecords = tQuestionMapper.hotPage(phone,DifficultyEnum.MEDIUM.getCode());
        List<QuestionPageVO> hardRecords = tQuestionMapper.hotPage(phone,DifficultyEnum.HARD.getCode());
        convertSortedList(easyRecords,typeCounts);
        convertSortedList(mediumRecords,typeCounts);
        convertSortedList(hardRecords,typeCounts);

        //计算各个难度的正确率
        CorrectRate correctRate=calculCorrectRate(limit);

        Set<QuestionPageVO> set=new HashSet<>();
        //防止set重复add
        addRecommend(set, correctRate.getEasyNum(),easyRecords);
        addRecommend(set,correctRate.getMediumNum(),mediumRecords);
        addRecommend(set, correctRate.getHardNum(),hardRecords);
//        addRecommend(set, stillNeed,mediumRecords);
        Map<Long, String> collect = tQuestionTypeService.getBaseMapper().selectList(null).stream().collect(Collectors.toMap(TQuestionType::getId, TQuestionType::getName));
        List<QuestionPageVO> res = set.stream()
                .map(item->{
                    String[] split = item.getType().split(",");
                    List<String> list=new ArrayList<>();
                    for(String s:split){
                        if(StringUtils.hasText(s)){
                            list.add(collect.get(Long.valueOf(s.trim())));
                        }
                    }
                    item.setTypes(list);
                    return item;
                })
                .collect(Collectors.toList());
        return res;
    }

    @Override
    public UserQuestionLogVo queryQuestionLog(String title,String language) {
        SysUser userInfo = UserInfo.getUserInfo();
        CodeTemplateEnum byLanguage = CodeTemplateEnum.getByLanguage(language);
        UserQuestionLogVo userQuestionLogVo = new UserQuestionLogVo();
        if(userInfo==null){
            userQuestionLogVo.setCodeShow(byLanguage.getCode());
            return userQuestionLogVo;
        }
        List<UserQuestionLog> userQuestionLogs = userQuestionLogMapper.selectList(new LambdaQueryWrapper<UserQuestionLog>()
                .eq(UserQuestionLog::getUserPhone, userInfo.getPhone())
                .eq(UserQuestionLog::getQuestionTitleSlug, title)
                .orderByDesc(UserQuestionLog::getId));
        //没选语言，不要代码模板
        if(StringUtils.isEmpty(language)||byLanguage==null){
            userQuestionLogVo.setUserQuestionLogList(userQuestionLogs);
            return userQuestionLogVo;
        }
        //有记录
        if(!CollectionUtils.isEmpty(userQuestionLogs)){
            for(UserQuestionLog userQuestionLog:userQuestionLogs){
                if(language.equals(userQuestionLog.getLanguage())){
                    userQuestionLogVo.setCodeShow(userQuestionLog.getCodeText());
                    break;
                }
            }
            if(StringUtils.isEmpty(userQuestionLogVo.getCodeShow())){
                String template=byLanguage.getTemplate();
                if(byLanguage==CodeTemplateEnum.JAVA){
                    Map<String,String> map=new HashMap<>();
                    map.put("className",TitleUtils.getValidTitle(title)+UserInfo.getUserInfo().getPhone());
                    StrSubstitutor strSubstitutor=new StrSubstitutor(map);
                    template = strSubstitutor.replace(byLanguage.getTemplate());
                }
                userQuestionLogVo.setCodeShow(template);
            }
            userQuestionLogVo.setUserQuestionLogList(userQuestionLogs);
        }
        //没记录，直接展示对应的代码模板
        else{
            String template=byLanguage.getTemplate();
            if(byLanguage==CodeTemplateEnum.JAVA){
                Map<String,String> map=new HashMap<>();
                map.put("className", TitleUtils.getValidTitle(title)+UserInfo.getUserInfo().getPhone());
                StrSubstitutor strSubstitutor=new StrSubstitutor(map);
                template = strSubstitutor.replace(byLanguage.getTemplate());
            }
            userQuestionLogVo.setCodeShow(template);
        }
        return userQuestionLogVo;
    }

    private CorrectRate calculCorrectRate(Integer limit){
        // 根据难度正确率，推荐不同难度的比例
        // 60 50 40 ->     （40+50）/2  (60+50+60)/3  （40+50）/2
        // 简单题推荐率和准确率成反比，也和中等题准确率成反比。
        // 难题推荐率和准确率成正比，也和中等题成正比。
        // 中等题和自身准确率成反比，和简单题成正比，和困难题成反比
        //27 7 0-> (73+93)/2, (93+27+100)/3  ,7
        Double easyRate = calculateRate(DifficultyEnum.EASY.getCode()).doubleValue();
        Double mediumRate = calculateRate(DifficultyEnum.MEDIUM.getCode()).doubleValue();
        Double hardRate = calculateRate(DifficultyEnum.HARD.getCode()).doubleValue();
        Double easyRecommendRate=((1-easyRate)*(1-mediumRate))/2.0;
        Double mediumRecommendRate=((1-mediumRate)+easyRate+(1-hardRate))/3.0;
        Double hardRecommendRate=(hardRate+mediumRate)/2.0;
        Double count=easyRecommendRate+mediumRecommendRate+hardRecommendRate;
        easyRate=easyRecommendRate/count;
        mediumRate=mediumRecommendRate/count;
        hardRate=hardRecommendRate/count;
        double easyNum= Math.round(easyRate*limit);
        double mediumNum=Math.round(mediumRate*limit);
//        double hardNum=Math.round(hardRate*limit);
        double hardNum=limit-easyNum-mediumNum;
        int stillNeed= (limit-(int)easyNum-(int)mediumNum-(int)hardNum);
        stillNeed=stillNeed<=0?0:stillNeed;
        CorrectRate correctRate = new CorrectRate();
        correctRate.setEasyNum((int) easyNum);
        correctRate.setMediumNum((int) mediumNum);
        correctRate.setHardNum((int) hardNum);
        return correctRate;
    }
    private void addRecommend(Set<QuestionPageVO> set,Integer limit ,List<QuestionPageVO> records){
        int originSize=set.size();
        while(set.size()<originSize+limit){
            Random r=new Random();
            int index=(2.0*limit/(double)records.size())>=1?records.size():2*limit;
            int i = r.nextInt(index);
            set.add(records.get(i));
        }
    }

    /**
     * 获取计算完权值后的排序题目
     * @param records
     * @param typeCounts
     * @return
     */
    private void convertSortedList(List<QuestionPageVO> records,Map<String,Long> typeCounts){
        //遍历计算权值并排序
        List<QuestionScoreDto> questionScoreDtos=new ArrayList<>();
        int index=records.size();
        //根据类型的权值+题目热门度
        for(QuestionPageVO record:records){
            String[] split = record.getType().split(",");
            QuestionScoreDto questionScoreDto = new QuestionScoreDto();
            Long cal=0L;
            for(String type:split){
                String trim = type.trim();
                if(typeCounts.containsKey(trim)){
                    cal+=typeCounts.get(trim)+(index--);
                }
            }
            questionScoreDto.setQuestionPageVO(record);
            questionScoreDto.setScore(cal);
            questionScoreDtos.add(questionScoreDto);
        }
        questionScoreDtos.sort(Comparator.comparing(QuestionScoreDto::getScore).reversed());
        records=questionScoreDtos.stream().map(QuestionScoreDto::getQuestionPageVO).collect(Collectors.toList());
    }
    private BigDecimal calculateRate(String difficulty){
        String phone = UserInfo.getUserInfo().getPhone();
        Long x = tQuestionMapper.queryLogCount(phone, difficulty, "Y");
        Long y = tQuestionMapper.queryLogCount(phone, difficulty, null);
        BigDecimal bigDecimalX = new BigDecimal(x);
        BigDecimal bigDecimalY = new BigDecimal(y);
        if(bigDecimalX.longValue()==0L){
            return new BigDecimal(0);
        }
        BigDecimal divide = bigDecimalX.divide(bigDecimalY,2, RoundingMode.HALF_DOWN).multiply(new BigDecimal(100));
        return divide;
    }


    public UserQuestionLog generateSubmitLog(CodeDO codeDO, CodeResult data) {
        UserQuestionLog userQuestionLog = new UserQuestionLog();
        userQuestionLog.setUserPhone(codeDO.getPhone());
        userQuestionLog.setCodeText(codeDO.getCodeText());
        userQuestionLog.setQuestionTitleSlug(codeDO.getTitle());
        userQuestionLog.setStatus(CodeLogStatusEnum.getLogStatusByCodeStatus(data.getStatus()));
        userQuestionLog.setCodeStatus(data.getStatus());
        userQuestionLog.setCorrectRate(data.getCorrectRate());
        userQuestionLog.setLanguage(codeDO.getLanguage());
        return userQuestionLog;
    }

    public void checkParams(TQuestionAddDto tQuestion) {
        Assert.notEmpty(tQuestion.getDifficulty(),"请填写难度");
        Assert.notEmpty(tQuestion.getTitle(),"请填写标题");
        Assert.notEmpty(tQuestion.getTypes(),"请填写类型");
        Assert.notEmpty(tQuestion.getTitleSlug(),"请填写题目标识");
        Assert.notEmpty(tQuestion.getDescription(),"请填写描述");
        Assert.notEmpty(tQuestion.getPayOnly(),"请填写资格");
//        Assert.notEmpty(tQuestion.getAnswerUrl(),"请填写答案");
//        Assert.notEmpty(tQuestion.getTestUrl(),"请填写测试用例");
        Assert.notNull(tQuestion.getLimitedTime(),"请填写时间限制");
        Assert.notNull(tQuestion.getLimitedMemory(),"请填写内存限制");
//        if(tQuestion.getAnswerFile().getSize()==0){
//            throw new MyException("请上传正确的答案文件");
//        }
//        if(tQuestion.getTestFile().getSize()==0){
//            throw new MyException("请上传正确的测试用例");
//        }
    }

    @Override
    public GlobalResponse<Boolean> clickLog() {
        List<UserQuestionClick> list=new ArrayList<>();
        AtomicInteger i=new AtomicInteger(0);
        RedisSerializer serializer = redisTemplate.getKeySerializer();
        redisTemplate.execute((RedisCallback) redisConnection -> {
            Cursor<byte[]> scan = redisConnection.scan(new ScanOptions.ScanOptionsBuilder().match(QuestionOnClickAop.CLICK + "*").count(100).build());
            while(scan.hasNext()){
                String deserialize = (String) serializer.deserialize(scan.next());
                String[] split = deserialize.split("::");
                String title=split[2];
                String phone=split[1];
                String value = redisUtils.getValue(deserialize);
                executeClick(list,title,phone,value);
                redisUtils.removeKey(deserialize);
                if(i.addAndGet(1)%100==0){
                    userQuestionClickService.saveOrUpdateBatch(list);
                    list.clear();
                }
//                if(i.get()>=5000){
//                    break;
//                }
            }
            if(CollectionUtils.isNotEmpty(list)){
                userQuestionClickService.saveOrUpdateBatch(list);
            }
            return null;
        });
        return null;
    }

    @Override
    @Transactional(rollbackFor = MyException.class)
    public void dailyQuestionGenerate(String date) {
        if(StringUtils.isEmpty(date)){
            date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        }
        DailyQuestion dailyQuestion = dailyQuestionMapper.getSpecificDateQuestion(date);
        if(dailyQuestion!=null){
            return;
        }
        Set<String> currentMonthQuestion = dailyQuestionMapper.getCurrentMonthQuestion(date)
                .stream().map(DailyQuestion::getTitleSlug).collect(Collectors.toSet());
        Integer integer = tQuestionMapper.selectCount(null);
        Random r=new Random();
        TQuestion tQuestion=null;
        do{
            int i = r.nextInt(integer);
            tQuestion = tQuestionMapper.selectById(i);
        }
        while(currentMonthQuestion.contains(tQuestion.getTitleSlug()));
        dailyQuestion = new DailyQuestion();
        dailyQuestion.setQuestionId(tQuestion.getId());
        dailyQuestion.setTitleSlug(tQuestion.getTitleSlug());
        try {
            dailyQuestion.setCurrentDateTime(new SimpleDateFormat("yyyy-MM-dd").parse(date));
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        dailyQuestionMapper.insert(dailyQuestion);

    }

    public void executeClick(List<UserQuestionClick> list,String title,String phone,String value){
        try{
            TQuestion tQuestion = this.baseMapper.selectOne(new LambdaQueryWrapper<TQuestion>()
                    .eq(TQuestion::getTitleSlug, title));
            String[] types = tQuestion.getType().split(",");
            for(String type:types){
                UserQuestionClick userQuestionClick = userQuestionClickService.getBaseMapper().selectOne(new LambdaQueryWrapper<UserQuestionClick>()
                        .eq(UserQuestionClick::getPhone, phone)
                        .eq(UserQuestionClick::getTypeId, type));
                if(userQuestionClick!=null){
                    userQuestionClick.setClickNum((userQuestionClick.getClickNum()==null?0:userQuestionClick.getClickNum())+Long.valueOf(value));
                }
                else{
                    userQuestionClick = new UserQuestionClick();
                    userQuestionClick.setTypeId(Long.valueOf(type.trim()));
                    userQuestionClick.setPhone(phone);
                    userQuestionClick.setClickNum(Long.valueOf(value));
                }
                list.add(userQuestionClick);
        }
        }catch (Exception e){
            log.error(e.getMessage());
        }
    }
}
